package kb;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import option.KBVocabulary;

import algebra.query.indexing.SecondaryIndex;

import utils.Util;

import entities.concept.Concept;
import entities.role.Role;

public class KBStat {
	private final DescriptionLogicsKB kb;
	private int nCAs, nInds, nRAs, nConcepts, nAxioms, nFeatures, nConstants;
	private Map<Role, Set<String>> fvSel = new HashMap<Role, Set<String>>();
	// cpCounts counts the possible instances that has this concept appearing in
	// the assertion
	// typically useful for features (f appears in a:C, then a mentions f)
	private Map<Concept, Integer> cpCounts = new HashMap<Concept, Integer>();
	private Map<Role, Integer> roleCounts = new HashMap<Role, Integer>();
	private Map<Role, Integer> fMentions = new HashMap<Role, Integer>();
	// for servlet to autocomplete
	private Set<String> conceptNames = new TreeSet<String>();
	private Set<String> roleNames = new TreeSet<String>();
	private Set<String> featureNames = new TreeSet<String>();
	private final double missingAtomCpCount= 0.2;
	private final double missingAtomRoleCount= 0.3; //likely a domain/range role?
	
	public Set<String> getConceptNamesInKB() {
		return conceptNames;
	}

	public Set<String> getRoleNamesInKB() {
		return roleNames;
	}

	public Set<String> getFeatureNamesInKB() {
		return featureNames;
	}

	public void addEntityNames(String s, String type) {
		s = s.trim();
		if (type.equals("role")) {
			roleNames.add(s);
		} else if (type.equals("feature")) {
			featureNames.add(s);
		} else if (type.equals("concept")) {
			conceptNames.add(s);
		}
	}

	/*
	 * For each materialized! SI with the same kb, check its C, if atomic, then
	 * add its # entry as the size of C
	 */
	private void populateSIStat() {
		for (SecondaryIndex si : SecondaryIndex.allCachedQueries.keySet()) {
			if (!si.getKB().equals(kb)) {
				continue;
			}
			Concept c = si.getCi();
			if (c.isAtomic() && si.getDescriptionIndex() != null
					&& !cpCounts.containsKey(c)) {
				setCpCounts(c, si.getDescriptionIndex()
						.getAllConceptAssertions().size());
			}
		}
	}

	public KBStat(DescriptionLogicsKB kb) {
		this.kb = kb;
		populateSIStat();
		this.nConstants = Util.getListOfConstantsInTbox().size();
	}

	public void setNCA(int ca) {
		this.nCAs = ca;
	}

	public void setNRA(int ra) {
		this.nRAs = ra;
	}

	public int getNumCA() {
		return this.nCAs;
	}

	public int getNumRA() {
		return this.nRAs;
	}

	public int getTotalConcepts() {
		return this.nConcepts;
	}

	public void incTotalConcepts() {
		nConcepts++;
	}

	// return # of instances of a primitive ? C in this KB
	public long getConceptSizeInKB(Concept c) {
		if (cpCounts.containsKey(c)) {
			return cpCounts.get(c);
		}
		//use a constant portion
		return Math.round(missingAtomCpCount*this.getTotalInstances());
	}

	// return # of possible successors of this role
	public int getRoleSizeInKB(Role r) {
		if (roleCounts.containsKey(r)) {
			return this.roleCounts.get(r);
		}
//		missingAtomRoleCount
		return 1;
	}

	// return # of possible successors of this role
	// hack for DC datasets, depending on the number of instances per product
	private static double inflate = 0;
	private static double avgSellers = 5;

	public int getFeatureMentionsInKB(Role f) {
		if (inflate == 0) {
			// extract the number
			String name = this.kb.getKBName();
//			if (name.startsWith(Builder.KRFilePrefix)) {
				int id = name.lastIndexOf('_');
				if (id > 0) {
					char tm = name.charAt(id - 1);
					if (Character.isDigit(tm)) {
						inflate = 1.0 + Character.digit(tm, 10);
					}
				}
//			}
		}
		if (fMentions.containsKey(f)) {
			return (int) Math.min(avgSellers * inflate * this.fMentions.get(f),
					this.getTotalInstances());
		}
		return 1;
	}

	/*
	 * return # of values of f in K todo: consider using histogram due to zipf's
	 * law
	 */
	public double getFeatureValueSizeInKB(Role f) {
		if (fvSel.containsKey(f)) {
			return 1.0 * fvSel.get(f).size();
		}
		// if missing, assume it is #constants/#fs
		return 1.0 * nConstants / this.nFeatures;
	}

	public int getTotalInstances() {
		if (this.nInds == 0) {
			this.nInds = kb.getAbox().getABoxIndividuals().size();
		}
		return this.nInds;
	}

	public void setNumAx(int na) {
		this.nAxioms = na;
	}

	public int getNumAx() {
		return this.nAxioms;
	}

	public int getNumFeatures() {
		return this.nFeatures;
	}

	public void incNumFeatures() {
		this.nFeatures++;
	}

	public void setFValues(Role f, String s) {
		Set<String> vs = null;
		if (this.fvSel.containsKey(f)) {
			vs = this.fvSel.get(f);
		} else {
			vs = new HashSet<String>();
		}
		vs.add(s);
		this.fvSel.put(f, vs);
	}

	public void incCpCounts(Concept c) {
		setCpCounts(c, 1);
	}

	public void setCpCounts(Concept c, int value) {
		Integer ci = null;
		if (this.cpCounts.containsKey(c)) {
			ci = this.cpCounts.get(c);
		} else {
			ci = new Integer(0);
		}
		ci += value;
		this.cpCounts.put(c, ci);
	}

	public void incRoleCounts(Role r) {
		Integer ri = null;
		if (this.roleCounts.containsKey(r)) {
			ri = this.roleCounts.get(r);
		} else {
			ri = new Integer(0);
		}
		ri++;
		this.roleCounts.put(r, ri);
	}

	// if the instance a ever mentions f
	public void incFCounts(Role f) {
		Integer fi = null;
		if (this.fMentions.containsKey(f)) {
			fi = this.fMentions.get(f);
		} else {
			fi = new Integer(0);
		}
		fi++;
		this.fMentions.put(f, fi);
	}

	// directly from parser, may include query constants
	public int getTotalConstants() {
		return Util.getListOfConstantsInTbox().size();
	}

	public void finalCounts() {
		// Top counts = # of all instances
		this.setCpCounts(KBVocabulary.CONCEPT_TOP, this.getTotalInstances());
	}

}
